Appendix A: Architecture Specifics Notes
Table of Contents
Overview
kernel严格区分架构相关和架构无关代码。 include/asm-arch
目录包含头文件, arch/arc
包含源代码。
架构相关基本分为两类:
- 架构相关独有调用的部分
- 架构无关调用的接口部分由各自架构定义。
Data types
内核在如下3个基本数据类型重做一些基本区别:
- 在C程序中标准的数据类型;如unsigned long,void *和char。
- 固定比特数的数据类型。定义在
<asm-arch/types.h>
typedef __signed__ char __s8; typedef unsigned char __u8; typedef __signed__ short __s16; typedef unsigned short __u16; typedef __signed__ int __s32; typedef unsigned int __u32; typedef signed char s8; typedef unsigned char u8; typedef signed short s16; typedef unsigned short u16; typedef signed int s32; typedef unsigned int u32; typedef signed long long s64; typedef unsigned long long u64;
- 不会直接使用的子系统特有的类型,但经常被特有的函数所用。比如类型 pidt用来处理pids和sectort用来指明扇区数。
The pre-processor constant __KERNEL__ must always be defined before the file is linked into the kernel source.
Alignment
在内核的某些地方可能需要处理非对齐数据类型。为了这目的,不同的体系需要定义2个不同的宏(在 <asm-arch/unaligned.h>
):
- getunaligned(ptr)
- putunaligned(val, ptr)
Memory Pages
asm-arch/page.h
to indicate the page size used:
- PAGESHIFT 页大小的指数
- PAGESIZE in bytes
- PAGEALIGN(addr) 使地址对齐页面
- clearpage(start) 填充null已删除以start开始的页
- copypage(to. from) 拷贝页数据从from到to
example:
#define PAGE_SHIFT 12 #ifdef __ASSEMBLY__ #define PAGE_SIZE (1 << PAGE_SHIFT) #else #define PAGE_SIZE (1UL << PAGE_SHIFT) #endif #define PAGE_MASK (~(PAGE_SIZE-1))
System Calls
促发一个system call从用户空间转化到内核空间机制有区别在支持的平台上。 <asm-arch/unistd.h>
- It defines pre-processor constants to link the descriptors of all system calls with symbolic constants.
- It defines functions to invoke system calls from within the kernel itself.
String Processing
<asm-arch/string.h>
The _HAVEARCHOPERATION macro must be set for each string operation that is defined as optimized by an architecture; for instance, _HAVEARCHMEMCPY must be set for memcpy. All nonimplemented functions are replaced with architecture-independent standard operations that are implemented in lib/string.c.
Thread Representation
The state of a running process is defined primarily by the contents of the processor registers. Processes that are not currently running must keep this data in corresponding data structures from which the data can be read and moved to the appropriate registers when the process is next activated by the scheduler.
<asm-arch/ptrace.h>
provides the ptregs structure to hold all registers that are placed on the kernel stack when the process switches from user mode to space mode as a result of a system call, an interrupt, or any other mechanism.<asm-arch/processor.h>
accommodates the threadstruct structure used to describe all other registers and all other task state information.<asm-arch/thread.h>
defines the threadinfo structure (not to be confused with threadstruct), which contains all task structure elements that the assembler code must access to implement kernel entry and exit.
ARM
The following definition of the ptregs structure consists simply of an array to hold the values of all
registers manipulated in kernel mode:
include/asm-arm/ptrace.h
/* * This struct defines the way the registers are stored on the * stack during a system call. Note that sizeof(struct pt_regs) * has to be a multiple of 8. */ struct pt_regs { long uregs[18]; };
The symbolic names of the registers and their positions within the array are defined by means of pre-
processor constants:
include/asm-arm/ptrace.h
#define ARM_cpsr uregs[16] #define ARM_pc uregs[15] #define ARM_lr uregs[14] #define ARM_sp uregs[13] #define ARM_ip uregs[12] #define ARM_fp uregs[11] #define ARM_r10 uregs[10] #define ARM_r9 uregs[9] #define ARM_r8 uregs[8] #define ARM_r7 uregs[7] #define ARM_r6 uregs[6] #define ARM_r5 uregs[5] #define ARM_r4 uregs[4] #define ARM_r3 uregs[3] #define ARM_r2 uregs[2] #define ARM_r1 uregs[1] #define ARM_r0 uregs[0] #define ARM_ORIG_r0 uregs[17]
include/asm-arm/processor.h
struct thread_struct { /* fault info */ unsigned long address; unsigned long trap_no; unsigned long error_code; /* debugging */ struct debug_info debug; };
However, machine instructions (in the form of their opcode) can be saved together with a memory
address for debugging purposes, as shown here:
include/asm-arm/processor.h
union debug_insn { u32 arm; u16 thumb; }; struct debug_entry { u32 address; union debug_insn insn; }; struct debug_info { int nsaved; struct debug_entry bp[2]; };
Bit Operations and Endianness
Manipulation of Bit Chains
内核中架构相关的比特处理在 <asmm-arch/bitops.h>
void set_bit(int nr, volatile unsigned long * addr) sets the bit at position nr; counting begins at addr. int test_bit(int nr, const volatile unsigned long * addr) checks whether the specified bit is set. void clear_bit(int nr, volatile unsigned long * addr) deletes the bit at position nr (count- ing begins at addr). ....
所有这些是原子函数,锁的陈述在相关汇编代码内。这些函数的非原子版本,以双下划线前缀。(_setbit).
Conversion between Byte Orders
The kernel provides the <byteorder/little_endian.h>
and <byteorder/big_endian.h>
files. The version for the current
processor is linked into <asm-arch/byteorder.h>
.
C default functions are defined in <byteoorder/swab.h>
, but they can be overwritten by a processor-specific implementation.
_arch_swab16, _arch_swab32, and _arch_swab64 swap the bytes between the representations,
and therefore convert big endian to little endian and vice versa. _swab16p, _arch_swab32p, and
_arch_swab64p do the same for pointer variables. swab stands for swap bytes.
<byteorder/little_endian.h>
#define __constant_htonl(x) ((__force __be32)___constant_swab32((x))) #define __constant_ntohl(x) ___constant_swab32((__force __be32)(x)) #define __constant_htons(x) ((__force __be16)___constant_swab16((x))) #define __constant_ntohs(x) ___constant_swab16((__force __be16)(x)) #define __constant_cpu_to_le64(x) ((__force __le64)(__u64)(x)) #define __constant_le64_to_cpu(x) ((__force __u64)(__le64)(x)) #define __constant_cpu_to_le32(x) ((__force __le32)(__u32)(x)) #define __constant_le32_to_cpu(x) ((__force __u32)(__le32)(x)) #define __constant_cpu_to_le16(x) ((__force __le16)(__u16)(x)) #define __constant_le16_to_cpu(x) ((__force __u16)(__le16)(x)) #define __constant_cpu_to_be64(x) ((__force __be64)___constant_swab64((x))) #define __constant_be64_to_cpu(x) ___constant_swab64((__force __u64)(__be64)(x)) #define __constant_cpu_to_be32(x) ((__force __be32)___constant_swab32((x))) #define __constant_be32_to_cpu(x) ___constant_swab32((__force __u32)(__be32)(x)) #define __constant_cpu_to_be16(x) ((__force __be16)___constant_swab16((x))) #define __constant_be16_to_cpu(x) ___constant_swab16((__force __u16)(__be16)(x)) #define __cpu_to_le64(x) ((__force __le64)(__u64)(x)) #define __le64_to_cpu(x) ((__force __u64)(__le64)(x)) #define __cpu_to_le32(x) ((__force __le32)(__u32)(x)) #define __le32_to_cpu(x) ((__force __u32)(__le32)(x)) #define __cpu_to_le16(x) ((__force __le16)(__u16)(x)) #define __le16_to_cpu(x) ((__force __u16)(__le16)(x)) #define __cpu_to_be64(x) ((__force __be64)__swab64((x))) #define __be64_to_cpu(x) __swab64((__force __u64)(__be64)(x)) #define __cpu_to_be32(x) ((__force __be32)__swab32((x))) #define __be32_to_cpu(x) __swab32((__force __u32)(__be32)(x)) #define __cpu_to_be16(x) ((__force __be16)__swab16((x))) #define __be16_to_cpu(x) __swab16((__force __u16)(__be16)(x))
Page Tables
To simplify memory management and to provide a memory model that
abstracts the differences between the various architectures, the ports
must offer functions to manipulate page tables and their entries. These
are declared in <asm-arch/pgtable.h>
.
Miscellaneous
Checksum Calculation
为包算checksums是IP网络中一个关键的算法时间。实现在
<asm-arch/checksum.h>
unsigned short ip_fast_csum calculates the requisite checksum based on the IP header and the header length. csum_partial calculates the checksum for a packet from the fragments that are received one by one.
Context Switch
<asm-arch/system.h>
void switch_to(struct task_struct *prev, struct task_struct *next, struct task_struct *last)
Finding the Current Process
The purpose of the current macro is to find a pointer to the task structure of the currently running process. It must be declared by each architecture in <asm-arch/current.h>.
Arm architecture:
include/asm-arm/current.h
static __always_inline struct task_struct * get_current(void) { return current_thread_info()->task; } #define current get_current()
The pointer to the current thread_info
instance found using
current_thread_info
is stored in register sp, as shown here:
include/asm-arm/thread_info.h
static inline struct thread_info *current_thread_info(void) { register unsigned long sp asm ("sp"); return (struct thread_info *)(sp & ~(THREAD_SIZE - 1)); }